package com.cskaoyan.javase.thread._4synchronized._1impl.method;

/**
 * 用原子操作的思想来解决多线程环境下的数据安全问题
 * 在多线程环境下，通过加锁的方式来实现一个原子操作,进而保证共享数据的安全.
 *
 * synchronized关键字实现线程同步,还可以使用同步方法
 * 同步方法就是synchronized关键字修饰的成员方法:
 * [访问权限修饰符] synchronized 返回值类型 方法名(形参){
 *     // 方法体
 * }
 * 解释:
 *      1.整个同步方法就是一个原子操作,相比较于同步代码块,同步方法有些场景下会更好用一些.
 *      2.同步方法实现加锁,也需要一个锁对象
 *      同样需要保证线程间共享同一个锁对象
 *      同步方法的锁对象,固定为当前类的this对象
 *
 *
 * @since 15:48
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        SellTicketTask task = new SellTicketTask();
        Thread t1 = new Thread(task, "窗口1");
        Thread t2 = new Thread(task, "窗口2");
        Thread t3 = new Thread(task, "窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellTicketTask implements Runnable {
    private int ticketNum = 1;
    private static final Object LOCK = new Object();

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        // 使用同步代码块保证多线程环境下的数据安全
        while (System.currentTimeMillis() - start < 10000) {
            if (ticketNum % 2 == 0) {
                // 偶数票,用同步代码块加锁保证线程同步
                synchronized (this) {
                    if (ticketNum <= 100) {
                        // 每次卖出一张票,休眠一段时间,模拟卖票需要耗费一点时间
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "卖出了第" + ticketNum + "张票!");
                        ticketNum++;
                    }
                }
            } else {
                // 奇数票
                sellTicket();
            }
        }
    }

    // 将卖票抽取为一个方法,定义成同步方法,卖票整个操作都变成了一个原子操作
    private synchronized void sellTicket() {
        if (ticketNum <= 100) {
            // 每次卖出一张票,休眠一段时间,模拟卖票需要耗费一点时间
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖出了第" + ticketNum + "张票!");
            ticketNum++;
        }
    }
}